package streamAPI;

import data.Employee;
import data.EmployeeData;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 1.Stream API vs 集合框架
 * > Stream API 关注的是多个数据的计算（排序、查找、过滤、映射、遍历等），面向CPU。
 * -集合关注的是数据的存储，面向内存的。
 * > Stream API 之于集合，类似于SQL之于数据表的查询。
 * <p>
 * 2.使用说明
 * ① Stream 自己不会存储元素。
 * ② Stream 不会改变源对象。相反，会返回一个持有结果的新Stream
 * ③ Stream 操作是延迟执行的。这意味着会等到需要结果的时候才执行。即一旦执行终止操作，就执行中间操作链，并产生
 * ④ Stream 一旦执行了终止操作，就不能再调用其它中间操作或终止操作了。
 * <p>
 * 3.Stream 执行流程
 * 步骤1：Stream的实例化
 * 步骤2：一系列的中间操作
 * 步骤3：执行终止操作
 */
public class StreamAPITest {
    /**
     * Stream 实例化
     */
    @Test
    public void test1() {
        /*
            创建方式1：通过集合
         */
        List<String> list = new ArrayList<>();
        list.add("asd");
        // default Stream<E> stream();  返回一个顺序流
        Stream<String> stream = list.stream();
        System.out.println(stream);

        // default Stream<E> parallelStream();  返回一个并行流
        Stream<String> parallelStream = list.parallelStream();
        System.out.println(parallelStream);
        /*
            创建方式2：通过数组
         */
        Integer[] integer = new Integer[]{1, 2, 3, 4, 7, 5};
        //   public static <T> Stream<T> stream(T[] array)
        Stream<Integer> integerStream = Arrays.stream(integer);

        //    public static IntStream stream(int[] array)
        int[] ints = new int[]{12, 23, 45, 9};
        IntStream intStream = Arrays.stream(ints);

        /*
            创建方式3：通过Stream的of()
         */
        Stream<String> stringStream = Stream.of("AA", "BB");

    }


    /**
     * 中间操作
     */
    @Test
    public void test2() {
        // 1.筛选与切片
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> stream = employees.stream();
        // 查询表中薪资大于7000的员工
        stream.filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);
        System.out.println();
        // limit(n)---截断流，使其元素不超过给定数量
        employees.stream().limit(6).forEach(System.out::println);
        System.out.println();

        // skip(n)---跳过元素，返回一个扔掉了前n个元素的流。若元素不足n个，则返回一个空流。
        employees.stream().skip(6).forEach(System.out::println);
        System.out.println();

        // distinct()--筛选，通过流所生成元素的hashCode()和equals()去除重复元素
        employees.add(new Employee(1009, "Leo", 66, 2500.32));
        employees.add(new Employee(1009, "Leo", 66, 2500.32));
        employees.add(new Employee(1009, "Leo", 66, 2500.32));

        employees.stream().distinct().forEach(System.out::println);
    }

    @Test
    public void test3() {
        // 2.映射
        List<String> list = Arrays.asList("aa", "bb", "Cc", "Dd", "ee");
        list.stream().map(String::toUpperCase).forEach(System.out::println);

        // 获取员工姓名长度大于3的姓名
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().map(Employee::getName).filter(name -> name.length() > 3)
                .forEach(System.out::println);
    }

    @Test
    public void test4() {
        // 3.排序
        List<Integer> list = Arrays.asList(1, 4, 32, 56, 78, 5, 3, 32, 11, 23);
        list.stream().sorted().forEach(i -> System.out.print(i + "\t"));
        System.out.println();

        int[] arr = new int[]{5, 4, 2, 1, 67, 89, 4343, 11, 231, 211, 1};
        Arrays.stream(arr).forEach(i -> System.out.print(i + "\t"));
        System.out.println();
        Arrays.stream(arr).sorted().forEach(i -> System.out.print(i + "\t"));

        EmployeeData.getEmployees().stream().sorted
                ((o1, o2) -> (int) (o2.getSalary() - o1.getSalary())).forEach(System.out::println);
    }

    /**
     * 终止操作
     */
    @Test
    public void test11() {
        // 1.匹配与查找
        List<Employee> employees = EmployeeData.getEmployees();
        // 是否所有员工的年龄都大于18
        // boolean allMatch(Predicate<? super T> predicate);
        System.out.println(employees.stream().allMatch((employee) -> employee.getAge() > 18));
        // boolean anyMatch(Predicate<? super T> predicate);
        boolean anyMatch = employees.stream().anyMatch(employee -> employee.getSalary() > 9000);
        System.out.println(anyMatch);//true

        // 返回第一个元素 Optional<T> findFirst();
        // Optional[Employee(id=1001, name=Tom, age=14, salary=6000.38)]
        System.out.println(employees.stream().findFirst());
        System.out.println(employees.stream().findFirst().get());

        // max(Comparator c) --返回流中最大值
        System.out.println(employees.stream().map(Employee::getSalary).max(Double::compareTo).get());
        // min(Comparator c) --返回流中最小值
        System.out.println(employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));

        // 返回流中元素的总个数 long count();
        System.out.println(employees.stream().count());

        // foreach(Consumer c)---内部迭代
        employees.stream().forEach(System.out::println);
        // 针对于集合，jdk8中增加了一个遍历的方法
        employees.forEach(System.out::println);// 针对list，遍历的方式：① Iterator ② 增强for ③ forEach() ④ 一般for

    }

    @Test
    public void test12() {
        // 2.归约
        // reduce(T identity, BinaryOperator)--可以将流中元素反复结合起来，得到一个值。返回T。
        // 练习：计算 1 - 10 的自然数之和
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //     T reduce(T identity, BinaryOperator<T> accumulator);
        System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2));
        System.out.println(list.stream().reduce(10, Integer::sum));

        //     Optional<T> reduce(BinaryOperator<T> accumulator);
        // 计算员工工资总和
        List<Employee> employees = EmployeeData.getEmployees();
        System.out.println(employees.stream().map(Employee::getSalary).reduce(Double::sum));
    }

    @Test
    public void test13() {
        // 3.收集
        List<Employee> employees = EmployeeData.getEmployees();
        // collect(Collector c) -- 将流转换为其他形式。接收一个Collector接口的实现，用于给Stream中元素做汇总的方法。
        // 练习一：查找工资大于6000的员工，结果返回一个List / Set
        List<Employee> collect = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());
        collect.forEach(System.out::println);
        System.out.println();
        employees.forEach(System.out::println);
        System.out.println();
        // 练习二：按照员工年龄排序，返回到一个新的List中
        List<Employee> employeeList = employees.stream().sorted(((o1, o2) -> o1.getAge() - o2.getAge())).collect(Collectors.toList());
        employeeList.forEach(System.out::println);

    }
}
